﻿using SharpDepend.Datatypes;
using System;
using System.Net;
using System.Net.Sockets;

namespace SharpDepend.WP8
{
    /// <summary>
    /// Reference: http://www.jarloo.com/c-udp-multicasting-tutorial/
    /// </summary>
    class MulticastManager : ISocketMulticastManager
    {
        class SocketMulticast : ISocketMulticast
        {
            // A client receiver for multicast traffic from any source
            UdpAnySourceMulticastClient _client = null;

            // true if we have joined the multicast group; otherwise, false
            bool _joined = false;

            // Buffer for incoming data
            private byte[] _receiveBuffer;

            // Maximum size of a message in this communication
            private const int MAX_MESSAGE_SIZE = 512;

            /// <summary>
            /// Create a new UdpAnySourceMulticastClient instance and join the group.
            /// </summary>
            [System.Diagnostics.DebuggerStepThrough]
            private BoolResult Join(string ip, int port)
            {
                try
                {
                    Exception wasError = null;

                    // Initialize the receive buffer
                    _receiveBuffer = new byte[MAX_MESSAGE_SIZE];

                    // Create the UdpAnySourceMulticastClient instance using the defined 
                    // GROUP_ADDRESS and GROUP_PORT constants. UdpAnySourceMulticastClient is a 
                    // client receiver for multicast traffic from any source, also known as Any Source Multicast (ASM)
                    _client = new UdpAnySourceMulticastClient(IPAddress.Parse(ip), port);

                    // Make a request to join the group.
                    _client.BeginJoinGroup(
                        result =>
                        {
                            try
                            {
                                // Complete the join
                                _client.EndJoinGroup(result);

                                // The MulticastLoopback property controls whether you receive multicast 
                                // packets that you send to the multicast group. Default value is true, 
                                // meaning that you also receive the packets you send to the multicast group. 
                                // To stop receiving these packets, you can set the property following to false
                                _client.MulticastLoopback = true;

                                // Set a flag indicating that we have now joined the multicast group 
                                _joined = true;

                                // Let others know we have joined by sending out a message to the group.
                                //Send("Joined the group");

                                // Wait for data from the group. This is an asynchronous operation 
                                // and will not block the UI thread.
                                //Receive();
                            }
                            catch (Exception error)
                            {
                                wasError = error;
                            }
                        }, null);

                    if (wasError != null)
                    {
                        throw wasError;
                    }

                    return BoolResult.Succeded;
                }
                catch (Exception error)
                {
                    return new BoolResult(error);
                }
            }

            public BoolResult CreateMulticastSender(string ip, int port)
            {
                return Join(ip, port);
            }

            public BoolResult CreateMulticastListener(string ip, int port)
            {
                return Join(ip, port);
            }

            public void Write(byte[] data, int length)
            {
                // Attempt the send only if you have already joined the group.
                if (_joined)
                {
                    _client.BeginSendToGroup(data, 0, data.Length,
                        result =>
                        {
                            _client.EndSendToGroup(result);
                        }, null);
                }
                else
                {
                    Log.Warning(this, "Multicast write failed since no group is joined.");
                }
            }

            public void Read(MulticastListenerResult result)
            {
                // Only attempt to receive if you have already joined the group
                if (_joined)
                {
                    Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);
                    _client.BeginReceiveFromGroup(_receiveBuffer, 0, _receiveBuffer.Length,
                        result2 =>
                        {
                            IPEndPoint source;

                            // Complete the asynchronous operation. The source field will 
                            // contain the IP address of the device that sent the message
                            _client.EndReceiveFromGroup(result2, out source);

                            result(_receiveBuffer);

                            // Call receive again to continue to "listen" for the next message from the group
                            //Receive();
                        }, null);
                }
                else
                {
                    Log.Warning(this, "Multicast receiving failed. Not joined to the group.");
                }
            }

            public void Dispose()
            {
                _client.Dispose();
            }
        }

        public ISocketMulticast CreateMulticast()
        {
            return new SocketMulticast();
        }
    }
}
